package com.feng.common.utils;

import lombok.experimental.UtilityClass;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * JSR-303校验
 *
 * @author: jack.Cheng
 * @date: 2022/9/22 15:13
 */
@UtilityClass
public class JSR303ValidateUtils {

    /**
     * JSR-303参数校验
     *
     * @param validateObject 待校验对象
     * @param <T>            泛型
     */
    public static <T> void validate(T validateObject) {
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(validateObject);
        ConstraintViolation<T> constraintViolation = getFirst(constraintViolations, null);
        if (constraintViolation != null) {
            throw new ValidationException(constraintViolation.getMessage());
        }
    }

    /**
     * Returns the first element in {@code iterable} or {@code defaultValue} if the iterable is empty.
     * The {@link Iterators} analog to this method is {@link Iterators#getNext}.
     *
     * <p>If no default value is desired (and the caller instead wants a {@link
     * NoSuchElementException} to be thrown), it is recommended that {@code
     * iterable.iterator().next()} is used instead.
     *
     * <p>To get the only element in a single-element {@code Iterable}, consider using {@link
     * #getOnlyElement(Iterable)} or {@link #getOnlyElement(Iterable, Object)} instead.
     *
     * <p><b>{@code Stream} equivalent:</b> {@code stream.findFirst().orElse(defaultValue)}
     *
     * @param defaultValue the default value to return if the iterable is empty
     * @return the first element of {@code iterable} or the default value
     * @since 7.0
     */
    public static <T> T getFirst(Iterable<? extends T> iterable, T defaultValue) {
        return getNext(iterable.iterator(), defaultValue);
    }

    /**
     * Returns the next element in {@code iterator} or {@code defaultValue} if the iterator is empty.
     * The {@link Iterables} analog to this method is {@link Iterables#getFirst}.
     *
     * @param defaultValue the default value to return if the iterator is empty
     * @return the next element of {@code iterator} or the default value
     * @since 7.0
     */
    public static <T> T getNext(Iterator<? extends T> iterator, T defaultValue) {
        return iterator.hasNext() ? iterator.next() : defaultValue;
    }
}
